package com.eshop.inventory.service.impl;

import com.eshop.inventory.request.ProductInventoryDBUpdateRequest;
import com.eshop.inventory.request.Request;
import com.eshop.inventory.request.RequestQueue;
import com.eshop.inventory.service.RequestAsyncProcessService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: Xukai
 * @description:
 * @createDate: 2018/7/23 16:12
 * @modified By:
 */
@Service
@Slf4j
public class RequestAsyncProcessServiceImpl implements RequestAsyncProcessService {

    RequestQueue requestQueue = RequestQueue.getInstance();
    ConcurrentHashMap<Integer, Boolean> flagMap = requestQueue.getFlagMap();

    @Override
    public void process(Request request) {
        try {
            // 做请求的路由，根据每个请求的商品id，路由到对应的内存队列中去
            ArrayBlockingQueue<Request> queue = getRoutingQueue(request.getProductId());
            // 将请求放入对应的队列中，完成路由操作
            // 使用put操作，当队列满时，发生阻塞，等待队列有空余后继续执行
            if (request instanceof ProductInventoryDBUpdateRequest) {
                log.info("添加异步更新数据库请求【ProductId = {}】至内存队列", request.getProductId());
            } else {
                log.info("添加异步刷新缓存请求【ProductId = {}】至内存队列", request.getProductId());
            }
            queue.put(request);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取路由到的内存队列
     *
     * @param productId 商品id
     * @return 内存队列
     */
    private ArrayBlockingQueue<Request> getRoutingQueue(Integer productId) {
        // 先获取productId的hash值
        String key = String.valueOf(productId);
        int h;
        int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        // 对hash值取模，将hash值路由到指定的内存队列中，比如内存队列大小10
        // 用内存队列的数量对hash值取模之后，结果一定是在0~9之间
        // 所以任何一个商品id都会被固定路由到同样的一个内存队列中去的
        int index = (requestQueue.getQueueSize() - 1) & hash;
        return requestQueue.getQueue(index);
    }

    @Override
    public void setFlag(Integer productId, Boolean flag) {
        flagMap.put(productId, flag);
    }

}
